/*@parser.c::*/
/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock ';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt
 */

#include <string.h>
#include <stdio.h>
#include "lexer.h"
#include "parser.h"
#include "symtab.h"
#include "pseudocode.h"
#include "keywords.h"
#include "tokens.h"

#define MAXSYMLIST      0x10000
#define MAXIDSIZ        32

char symlist[MAXSYMLIST][MAXIDSIZ];

char programName[MAXIDSIZ];
int sympos,procpos,funcpos,idposi,idposf,lblpos,cnstpos,typos;

int gLabel = 1,gConstLabel = 0;
int otype,dtype,returnedtype;
int negate;
int parfunc = 0;
int offset,varOffset = 0,funcOffset = 0;
int symbol_entry,function_entry;
int progSect;
int numFormalParms,parms[MAXPARMS] = {0,0,0,0};
int numParm;

int relopused,addopused,mulopused,acctypeused;
char floatval[10];

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  
  match(PROGRAM); 
  /** */strcpy(programName,lexeme);/** */
  match(ID);
  match(';');
  /** */
  progSect = PROGRAM;
  /** */
  block();
  match('.');
  /** */
  concattextasm(funcasm);
  exitgen();
  /** */
}

/* block -> declarations modules stmtblock*/
void block(void)
{
  declarations();
  /** */textgen();/** */
  modules();
  /** */funcgen("main");/** */
  stmtblock();
  /** */concatfuncasm(stmtasm);/** */
}

/* declarations -> 
 *    [LABEL UINT {',' UINT} ';']
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]*/
void declarations(void)
{
  if(lookahead==LABEL){
    match(LABEL);
    /** */
    lblpos = sympos;
    strcpy(symlist[sympos++],lexeme);
    /** */
    match(UINT);
    /** */otype = LBLTYPE;/** */
    /** */symtab_insert(symlist[lblpos],otype,0,gLabel++,0);/** */
    while(lookahead==','){
      match(',');
      /** */
      lblpos = sympos;
      strcpy(symlist[sympos++],lexeme);
      /** */
      match(UINT);
      /** */symtab_insert(symlist[lblpos],otype,0,0,0);/** */
    }
    match(';');
  }
  if(lookahead==CONST){
    match(CONST);
    /** */rodatagen();/** */
    /** */
    cnstpos = sympos;
    strcpy(symlist[sympos++],lexeme);
    /** */
    match(ID);
    /** */otype = CNSTTYPE;/** */
    match('=');
    constant();
    match(';');
    /** */symtab_insert(symlist[cnstpos],otype,dtype,0,0);/** */
    while(lookahead==ID){
       /** */
      cnstpos = sympos;
      strcpy(symlist[sympos++],lexeme);
      /** */
      match(ID);
      match('=');
      constant();
      match(';');
      /** */symtab_insert(symlist[cnstpos],otype,dtype,0,0);/** */
    }
  }
  if(lookahead==TYPE){
    match(TYPE);
    /** */
    typos = sympos;
    strcpy(symlist[sympos++],lexeme);
    /** */
    match(ID);
    /** */otype = TYPTYPE;/** */
    match('=');
    type();
    match(';');
    while(lookahead==ID){
      /** */
      typos = sympos;
      strcpy(symlist[sympos++],lexeme);
      /** */
      match(ID);
      match('=');
      type();
      match(';');
    }
  }
  if(lookahead==VAR){
    /** */
    otype = VARTYPE;
    bssgen(progSect);
    /** */
    match(VAR); 
rep_idlist:
    ///** */sympos=0/** */;
    idlist();match(':');type();match(';');
    if(lookahead==ID)goto rep_idlist;
  }
}

char readWriteLex[MAXLEN] = "";
char constLex[MAXLEN] = "";
/* constant -> ( ['+'|'-'] ID | UINT | FLOAT ) | ( ''' character* ''') */
void constant(void)
{
  /** */int isSigned=0;/** */
  if(lookahead=='+'){
    match('+');
    /** */isSigned=1;/** */
  }
  if(lookahead=='-'){
    match('-');
    /** */isSigned=1;/** */
  }
  switch(lookahead){
    case ID:
      match(ID);
      /* verificar se é identificador de uma contante (???) */
      break;
    case UINT:
      /** */strcpy(constLex,lexeme);/** */
      match(UINT);
      /** */dtype=INTEGER;/** */
      break;
    case FLOAT:
      match(FLOAT);
      /** */dtype=FLOAT;/** */
      break;
    default:
	/** */strcpy(readWriteLex,lexeme);/** */
	match(STRING);
	if(!isSigned){
	  /** */dtype=STRING;/** */
	} else {
	  fprintf(stderr, "ERROR: %d: unexpected operand!\n",line);
	  errFlag = PARSERERR;
	}
  }
}

/* modules -> {procedure | function} */
void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE){
      procedure();
      symbol_entry = symtab_lookup(symlist[procpos]);
    }else{
      function();
      symbol_entry = symtab_lookup(symlist[funcpos]);
    }
    /** */
    funcOffset = 0;
    varOffset = 0;
    symtab_removelocals(symbol_entry,&sympos);
    /** */
  }
}

/* procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';' */
void procedure(void)
{
  int i;
  match(PROCEDURE);
  /** **/
  numFormalParms = 0;
  parfunc = 0;
  numParm = 0;
  procpos = sympos;
  progSect = PROCEDURE;
  strcpy(symlist[sympos++],lexeme);
  funcgen(lexeme);
  /** **/
  match(ID);
  formalparm();
  match(';');
  /** */
  otype = PROCTYPE;
  if(symtab_lookup(symlist[procpos])) {
    fprintf(stderr,"ERROR:  %d: symbol \"%s\" already declared\n",
      line,symlist[procpos]);
    errFlag = SEMANTICERR;
  }else{
    funcOffset += 4;
    symtab_insert(symlist[procpos],otype,0,funcOffset,numFormalParms);
  }
  
  if(parfunc){
    int i=idposf-1;
    while(symtab_getOtype((symbol_entry = symtab_lookup(symlist[i]))) == PARMTYPE) {
      switch(symtab_getDtype(symbol_entry)){
	case INTEGER:
	case REAL:
	  funcOffset += 4;
	  symtab_setOffset(symbol_entry,funcOffset);
	  break;
      }
      i--;
    }
    for(i = 0;i < MAXPARMS;i++){
      symtab_setParm(symtab_lookup(symlist[procpos]),i+1,parms[i]);
      parms[i] = 0;
    }
  }
  /** */
  declarations();
  stmtblock();
  /** */concatfuncasm(stmtasm);/** */
  match(';');
  /** */endfuncgen(otype,NULL);/** */
}

/* function -> FUNCTION ID formalparm ':'  typeid ';' declarations stmtblock ';' */
void function(void)
{
  match(FUNCTION);
  /** */
  numFormalParms = 0;
  parfunc = 0;
  numParm = 0;
  funcpos = sympos;
  progSect = FUNCTION;
  strcpy(symlist[sympos++],lexeme);
  funcgen(lexeme);
  /** **/
  match(ID);
  formalparm();
  match(':');
  /** */otype = FUNCTYPE;/** */
  type(); 
  /** */
  if(parfunc){
    int i=idposf-1;
    while(symtab_getOtype((symbol_entry = symtab_lookup(symlist[i]))) == PARMTYPE) {
      switch(symtab_getDtype(symbol_entry)){
	case INTEGER:
	case REAL:
	  funcOffset += 4;
	  symtab_setOffset(symbol_entry,funcOffset);
	  break;
      }
      i--;
    }
    for(i = 0;i < MAXPARMS;i++){
      symtab_setParm(symtab_lookup(symlist[funcpos]),i+1,parms[i]);
      parms[i] = 0;
    }
  }

  /** */
  match(';');
  declarations();
  stmtblock();
  /** */concatfuncasm(stmtasm);/** */
  match(';');
  /** */endfuncgen(otype,symlist[funcpos]);/** */
}

/* formalparm -> '(' parlist ')' | "" */
void formalparm(void){
  if(lookahead =='('){
    match('(');
    parlist();
    match(')');
  }
}

/* parlist -> pardecl {';' pardecl}*/
void parlist(void){
  pardecl();
  while(lookahead == ';'){
    match(';');
    pardecl();
  }
}

/*pardecl -> [VAR] idlist ':' type | heading */
void pardecl(void){
  /** */otype = PARMTYPE;/** */
  switch(lookahead){
    case VAR:
      match(VAR);
      idlist();
      match(':');
      type();
    break;
    case ID:
      idlist();
      match(':');
      type();
    break;
    
    case PROCEDURE:
    case FUNCTION:
      heading();
    break;
  }
}

/* heading -> procedureheader | functionheader*/
void heading(void){
  if(lookahead==PROCEDURE) procedureheader();
  else functionheader();
}

/* procedureheader -> PROCEDURE ID formalparm */
void procedureheader(void){
  match(PROCEDURE);match(ID);formalparm();
}

/* functionheader -> FUNCTION ID formalparm ':' typeid */
void functionheader(void){
  match(FUNCTION);match(ID);formalparm();match(':');type();
}

/* idlist -> ID {','ID}*/
void idlist(void)
{
  /** */
  parfunc = 1;
  idposi = sympos;
  strcpy(symlist[sympos++],lexeme);
  declvargen(progSect,lexeme,4);
  if(otype == PARMTYPE) numFormalParms++;
  /** **/
  match(ID);
  while(lookahead==','){
    match(',');
    /** **/strcpy(symlist[sympos++],lexeme);/** **/
    /** **/
    declvargen(progSect,lexeme,4);
    if(otype == PARMTYPE) numFormalParms++;
    /** **/
    match(ID);
  }
  /** */idposf = sympos;/** */
}

/* type -> INTEGER | REAL */
void type(void)
{
  int i;
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      break;
    default:
      match(dtype=REAL);
  }
  
  /** */
  switch(otype){
    case VARTYPE:
      for(i=idposi;i<idposf;i++) {
	if(symbol_entry = symtab_lookup(symlist[i])) {
	  if(!(symtab_getOtype(symbol_entry) == VARTYPE && symtab_getOffset(symbol_entry) == 0)){ //sobreposição de variáveis
	    fprintf(stderr,"ERROR: %d: symbol \"%s\" already declared\n",
	      line,symlist[i]);
	    errFlag = SEMANTICERR;
	  }else{
	    switch(dtype){
	      case INTEGER:
	      case REAL:
		if(progSect != PROGRAM) varOffset -= 4;
		break;
	    }
	    symtab_insert(symlist[i],otype,dtype,varOffset,0);
	  }
	}else{
	  switch(dtype){
	    case INTEGER:
	    case REAL:
	      if(progSect != PROGRAM) varOffset -= 4;
	      break;
	  }
	  symtab_insert(symlist[i],otype,dtype,varOffset,0);
	}
      }
      break;
    case PARMTYPE:
      for(i=idposi;i<idposf;i++) {
	if(symbol_entry = symtab_lookup(symlist[i])) {
	  if(!(symtab_getOtype(symbol_entry) == VARTYPE)){ //sobreposição de variáveis
	    fprintf(stderr,"ERROR: %d: symbol \"%s\" already declared\n",
	      line,symlist[i]);
	    errFlag = SEMANTICERR;
	  }else{
	    symtab_insert(symlist[i],otype,dtype,0,0); //inserção após definir o retorno de função
	    parms[numParm] = dtype;
	    numParm++;
	  }
	}else{
	  symtab_insert(symlist[i],otype,dtype,0,0); //inserção após definir o retorno de função
	  parms[numParm] = dtype;
	  numParm++;
	}
      }
      break;
    case FUNCTYPE:
      if(symtab_lookup(symlist[funcpos])) {
	fprintf(stderr,"ERROR: %d: symbol \"%s\" already declared\n",
	  line,symlist[funcpos]);
	errFlag = SEMANTICERR;
      }else{
	switch(dtype){
	  case INTEGER:
	  case REAL:
	    funcOffset += 4;
	    break;
	}
	symtab_insert(symlist[funcpos],otype,dtype,funcOffset,numFormalParms);
      }
      break;
    case TYPTYPE:
      if(symtab_lookup(symlist[typos])) {
	fprintf(stderr,"ERROR: %d: symbol \"%s\" already declared\n",
	  line,symlist[typos]);
	errFlag = SEMANTICERR;
      }else{
	symtab_insert(symlist[typos],otype,dtype,0,0);
      }
      break;
  }
  /** */
}

/*  * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case FOR:
      forstmt();
      break;
    case GOTO:
      gotostmt();
      break;
    case CASE:
      casestmt();
      break;
    case UINT:
      labelstmt();
      break;
    case ID:
      idstmt();
      break;
    case WRITE:
      writestmt();
      break;
    case WRITELN:
      writelnstmt();
      break;
    case READ:
      readstmt();
      break;
    case READLN:
      readlnstmt();
      break;
  }
}

/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtlist -> stmt {';' stmt} */
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/* ifstmt -> IF expression THEN stmt [ELSE stmt] */
void ifstmt(void)
{
  int lbl = gLabel++;
  int lbl2 = lbl;
  match(IF);
  expression();
  /** */concatstmtasm(exprasm);/** */
  match(THEN);
  /** */jmpfalsegen(lbl,relopused);/** */
  stmt();
  if(lookahead==ELSE) {
    lbl2 = gLabel++;
    match(ELSE);
    /** */jmpgen(lbl2);/** */
    /** */lblflowgen(lbl);/** */
    stmt();
  }
  /** */lblflowgen(lbl2);/** */
}

/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  int lblHead = gLabel++;
  int lblTail = gLabel++;
  match(WHILE);
  /** */lblflowgen(lblHead);/** */
  expression();
  /** */concatstmtasm(exprasm);/** */
  match(DO);
  /** */jmpfalsegen(lblTail,relopused);/** */
  stmt();
  /** */jmpgen(lblHead);/** */
  /** */lblflowgen(lblTail);/** */
}

/* repstmt -> REPEAT stmt {';' stmt} UNTIL expression */
void repstmt(void)
{
  int lbl = gLabel++;
  match(REPEAT);
  /** */lblflowgen(lbl);/** */
  stmt();
  while(lookahead == ';'){
    match(';'); stmt();
  }
  match(UNTIL); expression();
  /** */concatstmtasm(exprasm);/** */
  /** */jmpfalsegen(lbl,relopused);/** */
}

/*
   forstmt -> FOR assgnmt to expr DO stmt
   to -> TO | DOWNTO
 */
void forstmt(void)
{
  int lblHead = gLabel++;
  int lblTail = gLabel++;
  match(FOR);
  /** **/
  char symbol[MAXLEN];
  strcpy(symbol,lexeme);
  
  if((symbol_entry = symtab_lookup(symbol)) == 0){
    fprintf(stderr,"ERROR: %d: symbol not found\n",line);
    errFlag = SEMANTICERR;
  }else{
    switch(symtab_getOtype(symbol_entry)){
      case VARTYPE:/** this is a variable **/
      case FUNCTYPE:/** this is a function call **/
      case PARMTYPE:
	break;
      default:
	fprintf(stderr,"ERROR: %d: symbol in ilegal context\n",line);
	errFlag = SEMANTICERR;
    }
  }
  /** **/
  match(ID);
  match(ASSGNMT);
  int progSectAnt = progSect;
  progSect = FOR;
  /** */
  expr();
  /** */
  concatstmtasm(exprasm);
  assigngen(symtab_getOffset(symbol_entry),symbol);
  lblflowgen(lblHead);
  int toflag = 0;
  /** */
  if (lookahead == TO){
    match(TO);
    /** */toflag = 1;/** */
  }else{
    match(DOWNTO);
  }
  expr();
  moveeaxtoecxgen();
  cmpforgen(symtab_getOffset(symbol_entry),symbol);
  /** */concatstmtasm(exprasm);/** */
  /** */
  if(toflag) jmptruegen(lblTail,'>');
  else  jmptruegen(lblTail,'<');
  /** */
  match(DO);
  stmt();
  /** */
  if(toflag) incecxgen(symtab_getOffset(symbol_entry),symbol);
  else dececxgen(symtab_getOffset(symbol_entry),symbol);
  jmpgen(lblHead);
  lblflowgen(lblTail);
  progSectAnt = progSect;
  /** */
}

/* gotostmt -> GOTO UINT */
void gotostmt(void)
{
  match(GOTO);
  
  /** */
  char symbol[MAXLEN];
  strcpy(symbol,lexeme);
  if((symbol_entry = symtab_lookup(lexeme))==0){
    fprintf(stderr,"ERROR: %d: symbol not found\n",line);
    errFlag = SEMANTICERR;
  }/*else{
    if(!symtab_getLabelUsed(symbol_entry)){
      fprintf(stderr,"ERROR: label %s is not used",symbol);
    }
  }*/
  /** */
  match(UINT);
  /** */jmplblgen(symtab_getLblNbr(symbol_entry));/** */
}

/* casestmt -> CASE expr OF constlist ':' stmt { ';' constlist ':' stmt } END */
void clause(void)
{
    constlist();
    match(':');
    stmt();
}

int lblCase = 0,lblCase2 = 0;
void casestmt(void)
{
    match(CASE);
    expr();
    pushgen(EXPR,0);
    /** */concatstmtasm(exprasm);/** */
    match(OF);
    int lblTail = gLabel++;
    lblCase = gLabel++;
    lblCase2 = lblCase;
    /** */
    clause();
    /** */jmpgen(lblTail);/** */
    /** */lblflowgen(lblCase2);/** */
    while (lookahead == ';') {
	match(';');
	lblCase = gLabel++;
	lblCase2 = lblCase;
        clause();
	/** */jmpgen(lblTail);/** */
	/** */lblflowgen(lblCase2);/** */
    }
    /** */
    lblflowgen(lblTail);
    /** */
    match(END);
}

void constlist(void){
  constant();
  /** */
  assignuintgen(INTCONV,constLex,NULL);
  relopgen();
  concatstmtasm(exprasm);
  jmptruegen(lblCase,'=');
  /** */
  while(lookahead==','){
    match(',');
    constant();
    /** */
    assignuintgen(INTCONV,constLex,NULL);
    relopgen();
    concatstmtasm(exprasm);
    jmptruegen(lblCase,'=');
    /** */
  }
  jmpgen(lblCase+1);
  lblflowgen(lblCase);
  lblCase = gLabel++;
  lblCase2 = lblCase;
}

/* labelstmt -> UINT ':' stmt */
void labelstmt(void){
  /** */
  char symbol[MAXLEN];
  strcpy(symbol,lexeme);
  if((symbol_entry = symtab_lookup(lexeme))==0){
    fprintf(stderr,"ERROR: %d: symbol not found\n",line);
    errFlag = SEMANTICERR;
  }else{
    if(symtab_getLabelUsed(symbol_entry)){
      fprintf(stderr,"ERROR: %d: label %s already used\n",line,symbol);
      errFlag = SEMANTICERR;
    }
  }
  /** */
  match(UINT);
  match(':');
  /** */
  lblgen(symtab_getLblNbr(symbol_entry));
  symtab_setLabelUsed(symbol_entry,1);
  /** */
  stmt();
}

/* idstmt -> ID [assgnmt expression | '(' exprlist ')'] */
void idstmt(void)
{
  /** **/
  char symbol[MAXLEN];
  strcpy(symbol,lexeme);
  
  if((symbol_entry = symtab_lookup(symbol)) == 0){
    fprintf(stderr,"ERROR: %d: symbol not found\n",line);
    errFlag = SEMANTICERR;
  }else{
    switch(symtab_getOtype(symbol_entry)){
      case VARTYPE:/** this is a variable **/
      case FUNCTYPE:/** this is a function call **/
      case PARMTYPE:
	break;
      default:
	fprintf(stderr,"ERROR: %d: symbol in ilegal context\n",line);
	errFlag = SEMANTICERR;
    }
  }
  /** **/
  match(ID); 
  if(lookahead=='('){
    /** this is the context for function/procedure call **/
    function_entry = symbol_entry;
    match('(');exprlist();match(')');
    /** subroutine call is here **/
    /** */
    concatstmtasm(parmasm);
    callgen(symbol,STMT);
    /** */
  }else if(lookahead==ASSGNMT){
    match(ASSGNMT); expression();
    /** */concatstmtasm(exprasm);/** */
    if((symbol_entry = symtab_lookup(symbol))==0){
      fprintf(stderr,"ERROR: symbol not found");
    }else{
      switch(symtab_getOtype(symbol_entry)){
	case VARTYPE:/** this is a variable/parameter assignment **/
	case PARMTYPE:
	  if((symtab_getDtype(symbol_entry) == INTEGER && acctypeused <= INTCONV)
	  || (symtab_getDtype(symbol_entry) == REAL && acctypeused <= FLOATCONV)){
	    offset=symtab_getOffset(symbol_entry);
	    assigngen(offset,symbol);
	  }else{
	    fprintf(stderr,"ERROR: %d: ilegal assignment\n",line);
	    errFlag = SEMANTICERR;
	  }
	  break;
	case FUNCTYPE:/** this is a function return **/
	  /** */jmplblconcgen(symbol);/** */
	  break;
	default:
	  fprintf(stderr,"ERROR: %d: symbol in ilegal context\n",line);
	  errFlag = SEMANTICERR;
      }
    }
  }else{
    /** this is the context for function call, without parameters **/
    /** subroutine call is here **/
    callgen(symbol,STMT);
  }
}

/* readstmt -> READ '(' idlist ')' */
void readstmt(void){
  match(READ);
  match('(');
  idlist();
  match(')');
}

/* readlnstmt -> READLN '(' idlist ')' */
void readlnstmt(void){
  match(READLN);
  match('(');
  idlist();
  match(')');
}

/* writestmt -> WRITE '(' constant { , constant } ')' */
void writestmt(void){
  int lbl;
  match(WRITE);
  match('(');
  constant();
  if(dtype == STRING){
    strconstgen(lbl=gConstLabel++,readWriteLex);
    printgen(lbl);
  }else{
    fprintf(stderr,"ERROR: %d: Sorry, Only string constants are already implemented for write/writeln\n",line);
    errFlag = PROGRAMERR;
  }
  while(lookahead==','){
    match(',');
    constant();
    if(dtype == STRING){
      strconstgen(lbl=gConstLabel++,readWriteLex);
      printgen(lbl);
    }else{
    fprintf(stderr,"ERROR: %d: Sorry, Only string constants are already implemented for write/writeln\n",line);
    errFlag = PROGRAMERR;
  }
  }
  match(')');
}

/* writelnstmt -> WRITELN '(' constant { , constant } ')' */
void writelnstmt(void){
  int lbl;
  match(WRITELN);
  match('(');
  constant();
  if(dtype == STRING){
    switch(lookahead){
      case ',':
	strconstgen(lbl=gConstLabel++,readWriteLex);
	printgen(lbl);
	break;
      case ')':
	strconstlngen(lbl=gConstLabel++,readWriteLex);
	printgen(lbl);
	break;
    }
  }else{
    fprintf(stderr,"ERROR: %d: Sorry, other types instead of string are not implemented yet for write/writeln\n",line);
    errFlag = PROGRAMERR;
  }
  while(lookahead == ','){
    match(',');
    constant();
    if(dtype == STRING){
      switch(lookahead){
	case ',':
	  strconstgen(lbl=gConstLabel++,readWriteLex);
	  printgen(lbl);
	  break;
	case ')':
	  strconstlngen(lbl=gConstLabel++,readWriteLex);
	  printgen(lbl);
	  break;
      }
    }else{
      fprintf(stderr,"ERROR: %d: Sorry, other types instead of string are not implemented yet for write/writeln\n",line);
      errFlag = PROGRAMERR;
    }
  }
  match(')');
}

/* relationalop -> '=' | '<' | '>' | GEQ | LEQ | NEQ */
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}

/* addop -> '+' | '-' | OR */
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}

/* mulop -> '*' | '/' | AND | DIV | MOD */
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}

/* expression -> expr [relationalop expr] */
void expression(void)
{
  /** an inherited attribute is get from the left side of
   * an assignment if any **/
  expr();
  if((relop = isrelationalop(lookahead))){
    /** */
    pushgen(EXPR,0);
    acctype = max(acctype,BOOLCONV);
    /** */
    match(lookahead); expr();
    /** */
    relopgen();
    relopused = relop;
    /** */
  }
  /** at this point we get a resulting data type as a
   * synthesized attribute **/
  /** */acctype = 0;/** */
}

/* expr -> [-] term {addop term} */
void expr(void)
{
  /** */if(progSect == FOR) acctype = 0;/** */
  if(lookahead=='-'){
    match('-');
    /** */negate = 1;/** */
  }
  term();
  /** */if(negate) neggen();/** */
  while((addop = isaddop(lookahead))){
    /** */
    if(addop == OR) acctype = max(acctype,BOOLCONV);
    addopused = addop;
    acctypeused = acctype;
    pushgen(EXPR,0);
    /** */
    match(lookahead); term();
    /** */addopgen(addopused,acctypeused);/** */
  }
  /** */negate = 0;/** */
}

/* term -> factor {mulop factor} */
void term(void)
{
  factor();
  while((mulop = ismulop(lookahead))){
    /** */
    if(mulop == AND) acctype = max(acctype,BOOLCONV);
    mulopused = mulop;
    acctypeused = acctype;
    pushgen(EXPR,0);
    /** */
    match(lookahead); factor();
    /** */mulopgen(mulopused,acctypeused);/** */
  }
}

/* factor -> UINT | FLOAT | ID ['(' exprlist ')'] | '(' expr ')' | NOT factor */
void factor(void){
    /** */
    char symbol[MAXLEN];
    strcpy(symbol,lexeme);
    /** */
    switch(lookahead){
      case UINT:
        /** **/
        acctype = max(acctype, INTCONV);
	acctypeused = acctype;
	assignuintgen(acctypeused,symbol,floatval);
        match(UINT);break;
      case FLOAT:
        /** **/
        acctype = max(acctype, FLOATCONV);
	acctypeused = acctype;
	assignfloatgen(acctypeused,symbol,floatval);
        match(FLOAT);break;
      case ID:
        /** in this context the symbol must be a variable or
         * a function using a symtab_lookup call **/
        /** **/
        if((symbol_entry = symtab_lookup(symbol))==0){
          fprintf(stderr,"ERROR: %d: symbol not found\n",line);
	  errFlag = SEMANTICERR;
        }else{
          switch(symtab_getOtype(symbol_entry)){
            case VARTYPE:/** this is a variable **/
	    case PARMTYPE:
	      switch(symtab_getDtype(symbol_entry)){
		case INTEGER:
		  acctype = max(acctype, INTCONV);
		  break;
		case REAL:
		  acctype = max(acctype, FLOATCONV);
		  break;
	      }
              break;
            case FUNCTYPE:/** this is a function call **/
	      switch(symtab_getDtype(symbol_entry)){
		case INTEGER:
		  acctype = max(acctype, INTCONV);
		  break;
		case REAL:
		  acctype = max(acctype, FLOATCONV);
		  break;
	      }
              break;
            default:
              fprintf(stderr,"ERROR: %d: symbol in ilegal context\n",line);
	      errFlag = SEMANTICERR;
          }
        }
        /** **/
        match(ID);
        if(lookahead=='('){
	  function_entry = symbol_entry;
          /** this is the context for function call **/
          match('(');exprlist();match(')');
          /** subroutine call is here **/
	  /** */
	  concatexprasm(parmasm);
	  callgen(symbol,EXPR);
	  /** */
        }else{
          /** this is the context for simple variable **/
          /** **/
	  if(symtab_getOtype(symbol_entry) == VARTYPE || symtab_getOtype(symbol_entry) == PARMTYPE){
	    offset=symtab_getOffset(symbol_entry);
	    varexprgen(offset,symbol);
	  }else if(symtab_getOtype(symbol_entry) == FUNCTYPE){
	    callgen(symbol,EXPR);
	  }
	  /** **/
        }
        break;
      case '(':
	/** **/
	returnedtype=acctypeused;
	/** **/
        match('(');
        expression();
        /** **/
	acctype = max(acctype, returnedtype);
	acctypeused = acctype;
	/** **/
        match(')');
        /** at this point, expression has synthesized an
         * attribute storing the resulting data type from the
         * expression. however, the just calculated attribute
         * might be greater than the l-attribute along the
         * current expression, so that a promotion must occur
         **/
         promotiongen(acctypeused);
        break;
      default:
	/** */acctype = max(acctype,BOOLCONV);/** */
        match(NOT);factor();
	notgen();
    }
}
/* exprlist -> expression {',' expression} */
void exprlist(void)
{
    /** */
    int numParm = 0;
    initparmauxasm();
    /** */
    /** this is the context for passing parameters to procedures
    * or functions */
    expression();
    if((symtab_getParm(function_entry,numParm+1) == INTEGER && acctypeused <= INTCONV)
      || (symtab_getParm(function_entry,numParm+1) == REAL && acctypeused <= FLOATCONV)){
      /** */concatparmauxasm(exprasm,numParm);/** */
      /** expression result stored in accumulator **/
      /** push the accumulator onto the stack **/
      /** */pushgen(PARM,numParm);/** */
    }else{
      fprintf(stderr,"ERROR: %d: wrong parameter type\n",line);
      errFlag = SEMANTICERR;
    }
    /** **/numParm++;/** **/

    while(lookahead==','){
      match(',');
      expression();
      if((numParm+1) <= symtab_getNumParms(function_entry)){
	if((symtab_getParm(function_entry,numParm+1) == INTEGER && acctypeused <= INTCONV)
	  || (symtab_getParm(function_entry,numParm+1) == REAL && acctypeused <= FLOATCONV)){
	  /** */concatparmauxasm(exprasm,numParm);/** */
	  /** expression result stored in accumulator **/
	  /** push the accumulator onto the stack **/
	/** */pushgen(PARM,numParm);/** */
	}else{
	  fprintf(stderr,"ERROR: %d: wrong parameter type\n",line);
	  errFlag = SEMANTICERR;
	}
      }
      /** **/numParm++;/** **/
    }
    /** */
    if((numParm+1) != symtab_getNumParms(function_entry)){
      concatparmasm();
    }else{
      fprintf(stderr,"ERROR: %d: wrong number of parameters\n",line);
      errFlag = SEMANTICERR;
    }
    /** */
}
